﻿namespace Network.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using Moq;

    using Shared;

    [TestClass]
    public class NetworkFixture
    {
        [TestMethod]
        public void ShouldCallToSendFramesIfAnyToTheNetwork()
        {
            const int NumberOfClients = 2;

            var frame1 = new Frame(It.IsAny<double>());
            var frame2 = new Frame(It.IsAny<double>());

            var frames = new Stack<Frame>();
            frames.Push(frame1);
            frames.Push(frame2);

            var algorithm = new Mock<IAlgorithm>();
            
            algorithm.SetupGet(x => x.Frames)
                .Returns(frames);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame1))
                .Returns(true);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame2))
                .Returns(true);

            Network network = new Network(algorithm.Object, It.IsAny<double>(), NumberOfClients, It.IsAny<double>());
            
            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            Action completed = () =>
            {
                algorithm.Verify(x => x.Send(frame1, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()), Times.Once());
                algorithm.Verify(x => x.Send(frame2, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()), Times.Once());
                manualResetEvent.Set();
            };

            network.Start(completed, progress => { });

            manualResetEvent.WaitOne(1500, false);
            manualResetEvent.Close();
        }

        //[TestMethod]
        //public void ShouldMaintainTheNumberOfTransmissions()
        //{
        //    const int NumberOfClients = 2;

        //    var frame1 = new Frame(It.IsAny<double>());
        //    var frame2 = new Frame(It.IsAny<double>());
        //    var frame3 = new Frame(It.IsAny<double>());

        //    var frames = new Stack<Frame>();
        //    frames.Push(frame1);
        //    frames.Push(frame2);
        //    frames.Push(frame3);

        //    var algorithm = new Mock<IAlgorithm>();

        //    algorithm.SetupGet(x => x.Frames)
        //        .Returns(frames);

        //    algorithm.Setup(x => x.WasTransmissionSuccessful(frame1))
        //        .Returns(false);

        //    algorithm.Setup(x => x.WasTransmissionSuccessful(frame2))
        //       .Returns(true);

        //    algorithm.Setup(x => x.WasTransmissionSuccessful(frame3))
        //      .Returns(false);

        //    Network network = new Network(algorithm.Object, It.IsAny<double>(), NumberOfClients, It.IsAny<double>(), new Mock<IDistribution>().Object);

        //    ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            
        //    Action completed = () =>
        //        {
        //            algorithm.Verify(x => x.Send(frame1, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<double>()), Times.Once());
        //            algorithm.Verify(x => x.Send(frame2, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<double>()), Times.Once());
        //            algorithm.Verify(x => x.Send(frame3, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<double>()), Times.Once());

        //            Assert.AreEqual(1, network.SuccesfulTransmissions);
        //            Assert.AreEqual(2, network.FailedTransmissions);
        //            manualResetEvent.Set();
        //        };

        //    network.Start(completed, progress => { });

        //    manualResetEvent.WaitOne(1500, false);
        //    manualResetEvent.Close();
        //}

        [TestMethod]
        public void ShouldSendNumberOfClientsToAlgorithm()
        {
            const int NumberOfClients = 2;

            var frame = new Frame(It.IsAny<double>());
            var frames = new Stack<Frame>();
            frames.Push(frame);

            var algorithm = new Mock<IAlgorithm>();

            algorithm.SetupGet(x => x.Frames)
                .Returns(frames);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame))
                .Returns(true);

            Network network = new Network(algorithm.Object, It.IsAny<double>(), NumberOfClients, It.IsAny<double>());

            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            Action completed = () =>
                {
                    algorithm.Verify(x => x.Send(It.IsAny<Frame>(), NumberOfClients, It.IsAny<double>(), It.IsAny<Action<int, double>>()), Times.Once());
                    manualResetEvent.Set();
                };

            network.Start(completed, progress => { });

            manualResetEvent.WaitOne(1500, false);
            manualResetEvent.Close();
        }

        [TestMethod]
        public void ShouldSendDeltaTimeToAlgorithm()
        {
            const int NumberOfClients = 1;
            const double NetworkSpeed = 1024;
            const double FrameSize = 256;

            var frame = new Frame(It.IsAny<double>());
            var frames = new Stack<Frame>();
            frames.Push(frame);

            var algorithm = new Mock<IAlgorithm>();

            algorithm.SetupGet(x => x.FrameSizeInKilobytes)
                .Returns(FrameSize);

            algorithm.SetupGet(x => x.Frames)
               .Returns(frames);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame))
                .Returns(false);

            Network network = new Network(algorithm.Object, It.IsAny<double>(), NumberOfClients, NetworkSpeed);

            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            Action completed = () =>
                {
                    algorithm.Verify(x => x.Send(It.IsAny<Frame>(), It.IsAny<int>(), FrameSize / NetworkSpeed, It.IsAny<Action<int, double>>()), Times.Once());
                    manualResetEvent.Set();
                };

            network.Start(completed, progress => { });
            manualResetEvent.WaitOne(1500, false);
            manualResetEvent.Close();
        }

        [TestMethod]
        public void ShouldMantainTotalElapsedTime()
        {
            const int NumberOfClients = 2;

            var frame1 = new Frame(It.IsAny<double>());
            var frame2 = new Frame(It.IsAny<double>());
            var frame3 = new Frame(It.IsAny<double>());

            var frames = new Stack<Frame>();
            frames.Push(frame1);
            frames.Push(frame2);
            frames.Push(frame3);

            var algorithm = new Mock<IAlgorithm>();

            algorithm.SetupGet(x => x.Frames).Returns(frames);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame1))
                .Returns(false);
            algorithm.Setup(x => x.Send(frame1, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()))
                .Returns(100);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame2))
                .Returns(true);
            algorithm.Setup(x => x.Send(frame2, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()))
                .Returns(250);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame3))
                .Returns(false);
            algorithm.Setup(x => x.Send(frame3, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()))
               .Returns(150);

            Network network = new Network(algorithm.Object, It.IsAny<double>(), NumberOfClients, It.IsAny<double>());

            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            Action completed = () =>
            {
                Assert.AreEqual(500, network.TotalElapsedTime);
                manualResetEvent.Set();
            };

            network.Start(completed, progress => { });
            manualResetEvent.WaitOne(1500, false);
            manualResetEvent.Close();
        }

        [TestMethod]
        public void ShouldMantainBandwith()
        {
            const int NumberOfClients = 1;

            var frame1 = new Frame(It.IsAny<double>());
            var frame2 = new Frame(It.IsAny<double>());
            var frame3 = new Frame(It.IsAny<double>());

            var frames = new Stack<Frame>();
            frames.Push(frame1);
            frames.Push(frame2);
            frames.Push(frame3);

            var algorithm = new Mock<IAlgorithm>();

            algorithm.SetupGet(x => x.FrameSizeInKilobytes).Returns(1000);
            algorithm.SetupGet(x => x.Frames).Returns(frames);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame1))
                .Returns(false);
            algorithm.Setup(x => x.Send(frame1, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()))
                .Returns(100);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame2))
                .Returns(true);
            algorithm.Setup(x => x.Send(frame2, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()))
                .Returns(250);

            algorithm.Setup(x => x.WasTransmissionSuccessful(frame3))
                .Returns(false);
            algorithm.Setup(x => x.Send(frame3, It.IsAny<int>(), It.IsAny<double>(), It.IsAny<Action<int, double>>()))
               .Returns(150);

            Network network = new Network(algorithm.Object, It.IsAny<double>(), NumberOfClients, It.IsAny<double>());

            ManualResetEvent manualResetEvent = new ManualResetEvent(false);
            Action completed = () =>
            {
                Assert.AreEqual(2, network.Bandwith);
                manualResetEvent.Set();
            };

            network.Start(completed, progress => { });
            manualResetEvent.WaitOne(1500, false);
            manualResetEvent.Close();
        }
    }
}
